﻿using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;

namespace MediaBot.Library
{
    [Serializable]
    public class WebPage: IDisposable
    {
        [NonSerialized] private Uri _uri;
        private readonly string _uriString;
        private string _content = string.Empty;
        private ArrayList _cookies;

        public WebPage(Uri uri)
        {
            Uri = uri;
            _uriString = uri.AbsoluteUri;
   
            Name = HashString(uri.AbsoluteUri);
            UpdateDestinationFileName();
            this.PostData = string.Empty;
            Encoding = Encoding.UTF8;
        }

        public virtual void Dispose()
        {
            this.ResponseCookie = null;
        }

        public CookieCollection ResponseCookie
        {
            get;
            internal set;
        }

        public CookieContainer RequestCookieContainer
        {
            get;
            set;
        }

        public Uri Uri
        {
            get { return _uri; }
            set { _uri = value; }
        }
        
        public string PostData
        {
            get;
            set;
        }

        public HttpStatusCode StatusCode { get; set; }

        public Encoding Encoding { get; set; }

        public string Name { get; private set; }

        public string DestinationFileName { get; private set; }

        private void UpdateDestinationFileName()
        {
            DestinationFileName = Guid.NewGuid().ToString() + ".html";
        }

        private string HashString(string text)
        {
            var hashedValue = 3074457345618258791ul;
            foreach (var t in text)
            {
                hashedValue += t;
                hashedValue *= 3074457345618258799ul;
            }
            return hashedValue.ToString(CultureInfo.InvariantCulture);
        }

        public void Save(string file)
        {
            File.WriteAllText(file, this._content);
        }

        public void Save()
        {
            File.WriteAllText( ConfigurationSettings.Settings.FileDownloadFolder + "\\" + this.DestinationFileName, this._content);
        }

        /// <summary>
        /// try to download URI with retry time, will throw exception if can't download in retry time
        /// </summary>
        /// <returns></returns>
        public string DownloadPage()
        {
            Uri url = null;
            var originalUrl = this.Uri;

            //continue downloading if this page redirect to another page
            do
            {
                var runningTime = 0;
                while (runningTime < ConfigurationSettings.Settings.Retry)
                {
                    try
                    {
                        runningTime++;
                        this._content = this.GetPageContent(originalUrl, out url);
                        url = null;
                        break;
                    }
                    catch (WebException ex)
                    {
                        if (ex.Message.IndexOf("Server Unavailable") >= 0)
                            System.Threading.Thread.Sleep(60 * 1000);
                        else if (runningTime >= ConfigurationSettings.Settings.Retry)
                            throw ex;
                    }
                    catch (Exception ex)
                    {
                        //if (ex is System.Net.WebException)
                        //{
                        //    if (System.Text.RegularExpressions.Regex.Match(ex.Message, "404.+Forbidden") != null)
                        //        throw new ForbiddenException();
                        //}
                        if (runningTime >= ConfigurationSettings.Settings.Retry)
                            throw ex;
                    }
                }
                originalUrl = url;
            }
            while (url != null);

            return this._content;
        }

        public virtual void Download()
        {
            this.DownloadPage();
        }

        private void InitializeWebRequest(HttpWebRequest request)
        {
            //request.UserAgent = "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)";
            request.UserAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36";
            request.KeepAlive = true;
            request.AllowAutoRedirect = true;
            //request.ContentType = "application/x-www-form-urlencoded";
            //request.ContentType = "text/html";

            if (this.RequestCookieContainer != null)
                request.CookieContainer = this.RequestCookieContainer;
            else
                request.CookieContainer = new CookieContainer();
            if (this._cookies != null)
            {
                var scheme = this.Uri.Scheme;
                foreach (Cookie cookie in this._cookies)
                {
                    request.CookieContainer.Add(new Uri(scheme +"://" + request.Host), new Cookie(cookie.Name, cookie.Value));
                }
            }
            
            // set request timeout
            //request.Timeout = ConfigurationSettings.Settings.RequestTimeout;
            if (this.PostData != string.Empty)
            {
                request.ContentType = "application/x-www-form-urlencoded";
                request.Method = WebRequestMethods.Http.Post;
                request.ContentLength = this.PostData.Length;

                var rStream = request.GetRequestStream();
                using (var sw = new StreamWriter(rStream))
                {
                    sw.Write(PostData);
                    sw.Close();
                }
            }
            else
                request.Method = WebRequestMethods.Http.Get;
            
        }

        private string GetPageContent(Uri uri, out Uri redirectUri)
        {
            redirectUri = null;
            try
            {
                string content;
                var request = (HttpWebRequest)WebRequest.Create(uri);

                InitializeWebRequest(request);

                // retrieve response from web request
                using (var response = (HttpWebResponse)request.GetResponse())
                {
                    var charset = response.CharacterSet;

                    this.StatusCode = response.StatusCode;

                    if (!string.IsNullOrEmpty(charset))
                    {
                        try
                        {
                            Encoding = Encoding.GetEncoding(charset);
                        }
                        catch { }
                    }

                    using (var webResponseStream = response.GetResponseStream())
                    {                        
                        // Reading the webResponseStream with streamReader object and assigning into one string
                        using (var streamReader = new StreamReader(webResponseStream, Encoding))
                        {
                            content = streamReader.ReadToEnd();
                        }
                    }

                    this.RequestCookieContainer = request.CookieContainer;
                    if (this.RequestCookieContainer != null)
                        this.RequestCookieContainer.Add(response.Cookies);

                    if (!uri.Equals(response.ResponseUri))
                        redirectUri = response.ResponseUri;
                }

                return content;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void AddCookie(string name, string value)
        {
            if (this._cookies == null)
                this._cookies = new ArrayList();
            this._cookies.Add(new Cookie(name, value));
        }
    }
}